// $(document).ready(() => {
//     var v = new Viewer('assets');
//     // var screen_height = Math.max( document.body.scrollHeight, document.body.offsetHeight, 
//     //                            document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight );   
//     // $("#footer").css("top",screen_height - $("#footer").height() - 20);
// });

import { L2D } from "./l2d.js";
const fs = window.require("fs")
function toArrayBuffer(buffer) {
    var ab = new ArrayBuffer(buffer.length);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buffer.length; ++i) {
        view[i] = buffer[i];
    }
    return ab;
}

function fixToCanvas(w,h, canvasW, canvasH) {
    let x = 0, y = 0
    
    if(w/h>canvasW/canvasH) {
        w = (canvasH/h) * w
        h = canvasH
    }
    else {
        h = (canvasW/w) * h
        w = canvasW
    }

    x = canvasW - w
    y = canvasH - h
    return [x,y,w,h]
}

export class Viewer {
    constructor (modeldir) {
        // this.l2d = new L2D(modeldir);

        this.basePath = modeldir;
        this.loader = new PIXI.loaders.Loader(this.basePath);
        this.animatorBuilder = new LIVE2DCUBISMFRAMEWORK.AnimatorBuilder();
        this.timeScale = 1;
        this.models = {};

        this.drawBBox = false

        // this.canvas = $(".Canvas");
        // this.selectCharacter = $(".selectCharacter");
        // this.selectAnimation = $(".selectAnimation");

        // let stringCharacter = "<option>Select</option>";
        // for (let val in charData) {
        //     stringCharacter+= '<option value="' + charData[val] + '">' + val + '</option>';
        // }
        // this.selectCharacter.html(stringCharacter);
        // this.selectCharacter.change((event) => {
        //     if (event.target.selectedIndex == 0) {
        //         return;
        //     }
        //     let name = event.target.value;
        //     this.l2d.load(name, this);
        // });

        this.width = 320;
        this.height = 240;

        this.app = new PIXI.Application(this.width, this.height, { backgroundColor: 0xffffff, preserveDrawingBuffer: true });
        
        this.app.view.style.width = this.width + "px";
        this.app.view.style.height = this.height + "px";
        this.app.renderer.resize(this.width, this.height);
        // this.canvas.html(this.app.view);

        this.app.ticker.add((deltaTime) => {
            if (!this.model) {
                return;
            }

            this.model.update(deltaTime);
            this.model.masks.update(this.app.renderer);
            
            if(this.drawBBox) {
                let rect = this.model.getBounds()
                this.bbox.clear()
                this.bbox.lineStyle(1, 0xFF0000)
                this.bbox.drawRect(rect.x, rect.y, rect.width, rect.height)
            }

            this.onFrame && this.onFrame()
        });

        this.isClick = false;
        this.app.view.addEventListener('mousedown', (event) => {
            this.isClick = true;
        });
        this.app.view.addEventListener('mousemove', (event) => {
            if (this.isClick) {
                this.isClick = false;
                if (this.model) {
                    this.model.inDrag = true;
                }
            }

            if (this.model) {
                let mouse_x = this.model.position.x - event.offsetX;
                let mouse_y = this.model.position.y - event.offsetY;
                this.model.pointerX = -mouse_x / this.app.view.height;
                this.model.pointerY = -mouse_y / this.app.view.width;
            }
        });
        this.app.view.addEventListener('mouseup', (event) => {
            if (!this.model) {
                return;
            }

            if (this.isClick) {
                if (this.isHit('TouchHead', event.offsetX, event.offsetY)) {
                    this.startAnimation("touch_head", "base");
                } else if (this.isHit('TouchSpecial', event.offsetX, event.offsetY)) {
                    this.startAnimation("touch_special", "base");
                } else {
                    const bodyMotions = ["touch_body", "main_1", "main_2", "main_3"];
                    let currentMotion = bodyMotions[Math.floor(Math.random()*bodyMotions.length)];
                    this.startAnimation(currentMotion, "base");
                }
            }

            this.isClick = false;
            this.model.inDrag = false;
        })
        
        this.bbox = new PIXI.Graphics()
    }

    setBg(sprite) {

        if(this.bg) {
            this.app.stage.removeChild(this.bg)
        }
        this.bg = sprite

        this.app.stage.addChild(this.bg)
        this.app.stage.setChildIndex(this.bg, 0)

        ;[this.bg.x,this.bg.y,this.bg.width,this.bg.height]
            = fixToCanvas(this.bg.width,this.bg.height,this.width,this.height)
    }

    changeCanvas (model) {
        this.app.stage.removeChildren();

        // 动画
        // this.selectAnimation.empty();
        // model.motions.forEach((value, key) => {
        //     if (key != "effect") {
        //         let btn = document.createElement("button");
        //         let label = document.createTextNode(key);
        //         btn.appendChild(label);
        //         btn.className = "btnGenericText";
        //         btn.addEventListener("click", () => {
        //             this.startAnimation(key, "base");
        //         });
        //         this.selectAnimation.append(btn);
        //     }
        // });

        if(this.bg) {
            this.app.stage.addChild(this.bg)
        }

        this.model = model;
        this.model.update = this.onUpdate; // HACK: use hacked update fn for drag support
        // console.log(this.model);
        this.model.animator.addLayer("base", LIVE2DCUBISMFRAMEWORK.BuiltinAnimationBlenders.OVERRIDE, 1);

        this.app.stage.addChild(this.model);
        this.app.stage.addChild(this.model.masks);

        this.onresize();

        this.app.stage.addChild(this.bbox)
    }

    resize(w, h) {
        this.width = w
        this.height = h
        this.onresize()
    }

    onresize() {
        console.log("live2d onresize")

        // if (event === void 0) { event = null; }

        this.app.view.style.width = this.width + "px";
        this.app.view.style.height = this.height + "px";
        this.app.renderer.resize(this.width, this.height);

        if (this.model) {
            this.model.position = new PIXI.Point((this.width * 0.5), (this.height * 0.5));
            this.model.scale = new PIXI.Point((this.model.position.x * 0.06), (this.model.position.x * 0.06));
            this.model.masks.resize(this.app.view.width, this.app.view.height);
            
            if(this.model.height <= 200) {
                this.model.scale = new PIXI.Point((this.model.position.x * 0.6), (this.model.position.x * 0.6));
            }
        }
    }

    onUpdate (delta) {
        let deltaTime = 0.016 * delta;

        if (!this.animator.isPlaying) {
            let m = this.motions.get("idle");
            this.animator.getLayer("base").play(m);
        }
        this._animator.updateAndEvaluate(deltaTime);

        if (this.inDrag) {
            this.addParameterValueById("ParamAngleX", this.pointerX * 30);
            this.addParameterValueById("ParamAngleY", -this.pointerY * 30);
            this.addParameterValueById("ParamBodyAngleX", this.pointerX * 10);
            this.addParameterValueById("ParamBodyAngleY", -this.pointerY * 10);
            this.addParameterValueById("ParamEyeBallX", this.pointerX);
            this.addParameterValueById("ParamEyeBallY", -this.pointerY);
        }

        if (this._physicsRig) {
            this._physicsRig.updateAndEvaluate(deltaTime);
        }

        this._coreModel.update();

        let sort = false;
        for (let m = 0; m < this._meshes.length; ++m) {
            this._meshes[m].alpha = this._coreModel.drawables.opacities[m];
            this._meshes[m].visible = Live2DCubismCore.Utils.hasIsVisibleBit(this._coreModel.drawables.dynamicFlags[m]);
            if (Live2DCubismCore.Utils.hasVertexPositionsDidChangeBit(this._coreModel.drawables.dynamicFlags[m])) {
                this._meshes[m].vertices = this._coreModel.drawables.vertexPositions[m];
                this._meshes[m].dirtyVertex = true;
            }
            if (Live2DCubismCore.Utils.hasRenderOrderDidChangeBit(this._coreModel.drawables.dynamicFlags[m])) {
                sort = true;
            }
        }

        if (sort) {
            this.children.sort((a, b) => {
                let aIndex = this._meshes.indexOf(a);
                let bIndex = this._meshes.indexOf(b);
                let aRenderOrder = this._coreModel.drawables.renderOrders[aIndex];
                let bRenderOrder = this._coreModel.drawables.renderOrders[bIndex];

                return aRenderOrder - bRenderOrder;
            });
        }

        this._coreModel.drawables.resetDynamicFlags();
    }

    startAnimation (motionId, layerId) {
        if (!this.model) {
            return;
        }

        let m = this.model.motions.get(motionId);
        if (!m) {
            return;
        }

        let l = this.model.animator.getLayer(layerId);
        if (!l) {
            return;
        }

        l.play(m);
    }

    isHit (id, posX, posY) {
        if (!this.model) {
            return false;
        }

        let m = this.model.getModelMeshById(id);
        if (!m) {
            return false;
        }

        const vertexOffset = 0;
        const vertexStep = 2;
        const vertices = m.vertices;

        let left = vertices[0];
        let right = vertices[0];
        let top = vertices[1];
        let bottom = vertices[1];

        for (let i = 1; i < 4; ++i) {
            let x = vertices[vertexOffset + i * vertexStep];
            let y = vertices[vertexOffset + i * vertexStep + 1];

            if (x < left) {
                left = x;
            }
            if (x > right) {
                right = x;
            }
            if (y < top) {
                top = y;
            }
            if (y > bottom) {
                bottom = y;
            }
        }

        let mouse_x = m.worldTransform.tx - posX;
        let mouse_y = m.worldTransform.ty - posY;
        let tx = -mouse_x / m.worldTransform.a;
        let ty = -mouse_y / m.worldTransform.d;

        return ((left <= tx) && (tx <= right) && (top <= ty) && (ty <= bottom));
    }

    
    setPhysics3Json (value) {
        if (!this.physicsRigBuilder) {
            this.physicsRigBuilder = new LIVE2DCUBISMFRAMEWORK.PhysicsRigBuilder();
        }
        this.physicsRigBuilder.setPhysics3Json(value);

        return this;
    }
    
    load (name, callback) {
        if (!this.models[name]) {
            let modelDir = name+'/';
            let modelPath = name+'.model3.json';
            if(!fs.existsSync(this.basePath+'/'+modelDir+modelPath)) {
                throw new Error("not found file .model3.json")
            }

            let textures = new Array();
            let textureCount = 0;
            let motionNames = new Array();
            let modelNames = new Array();

            //if (!modelNames.includes(name+'_model')){
                this.loader.add(name+'_model', modelDir+modelPath, { xhrType: PIXI.loaders.Resource.XHR_RESPONSE_TYPE.JSON });
                modelNames.push(name+'_model');
            //} 

            this.loader.load((loader, resources) => {
                let groups = null;

                let model3Obj = resources[name+'_model'].data;
                if(model3Obj) {
                    if(model3Obj.FileReferences) {
                        if (model3Obj.FileReferences.Moc) {
                            loader.add(name+'_moc', modelDir+model3Obj['FileReferences']['Moc'], { xhrType: PIXI.loaders.Resource.XHR_RESPONSE_TYPE.BUFFER });
                        }

                        if (typeof(model3Obj['FileReferences']['Textures']) !== "undefined") {
                            model3Obj['FileReferences']['Textures'].forEach((element) => {
                                loader.add(name+'_texture'+textureCount, modelDir+element);
                                textureCount++;
                            });
                        }

                        if (typeof(model3Obj['FileReferences']['Physics']) !== "undefined") {
                            loader.add(name+'_physics', modelDir+model3Obj['FileReferences']['Physics'], { xhrType: PIXI.loaders.Resource.XHR_RESPONSE_TYPE.JSON });
                        }

                        if (typeof(model3Obj['FileReferences']['Motions']) !== "undefined") {
                            for (let group in model3Obj['FileReferences']['Motions']) {
                                model3Obj['FileReferences']['Motions'][group].forEach((element) => {
                                    let motionName = element['File'].split('/').pop().split('.').shift();
                                    if (!motionNames.includes(name+'_'+motionName)){
                                        loader.add(name+'_'+motionName, modelDir+element['File'], { xhrType: PIXI.loaders.Resource.XHR_RESPONSE_TYPE.JSON });
                                        motionNames.push(name+'_'+motionName);
                                    } else {
                                        var n = name+'_'+motionName+String(Date.now());
                                        loader.add(n, modelDir+element['File'], { xhrType: PIXI.loaders.Resource.XHR_RESPONSE_TYPE.JSON });
                                        motionNames.push(name+'_'+motionName);
                                    }
                                });
                            }
                        }
                    }

                    if (typeof(model3Obj['Groups'] !== "undefined")) {
                        groups = LIVE2DCUBISMFRAMEWORK.Groups.fromModel3Json(model3Obj);
                    }
                }

                loader.load((l, r) => {
                    let moc = null;
                    // nwjs 可能加载不到
                    if(!r[name+'_moc'].data) {
                        console.log(r[name+'_moc'].url)
                        r[name+'_moc'].data = toArrayBuffer(fs.readFileSync(r[name+'_moc'].url))
                    }
                    if (typeof(r[name+'_moc']) !== "undefined") {
                        moc = Live2DCubismCore.Moc.fromArrayBuffer(r[name+'_moc'].data);
                    }

                    if (typeof(r[name+'_texture'+0]) !== "undefined") {
                        for (let i = 0; i < textureCount; i++) {
                            textures.splice(i, 0, r[name+'_texture'+i].texture);
                        }
                    }

                    if (typeof(r[name+'_physics']) !== "undefined") {
                        this.setPhysics3Json(r[name+'_physics'].data);
                    }

                    let motions = new Map();
                    motionNames.forEach((element) => {
                        let n = element.split(name+'_').pop();
                        motions.set(n, LIVE2DCUBISMFRAMEWORK.Animation.fromMotion3Json(r[element].data));
                    });

                    let model = null;
                    let coreModel = Live2DCubismCore.Model.fromMoc(moc);
                    if (coreModel == null) {
                        return;
                    }

                    let animator = this.animatorBuilder
                        .setTarget(coreModel)
                        .setTimeScale(this.timeScale)
                        .build();

                    let physicsRig = this.physicsRigBuilder
                        .setTarget(coreModel)
                        .setTimeScale(this.timeScale)
                        .build();

                    let userData = null;

                    model = LIVE2DCUBISMPIXI.Model._create(coreModel, textures, animator, physicsRig, userData, groups);
                    model.motions = motions;
                    this.models[name] = model;

                    callback && callback(model);
                });
            });
        } else {
            callback && callback(this.models[name]);
        }
        
    }
}

// function onSelectBG(){
//     console.log(window.pageXOffset + " : " + window.pageYOffset);
//     var div = document.createElement('div');
//     div.className = "darken";
//     div.id = "darken";
//     div.style.top = window.pageYOffset + "px";
//     div.addEventListener("click", function(e) {
//             document.body.removeChild(document.getElementById("selector"));
//             document.body.removeChild(document.getElementById("darken"));
//             document.body.style.overflow = "auto";
//         }, false);
//     document.body.appendChild(div);
//     document.body.style.overflow = "hidden";
//     var selector = document.createElement('div');
//     selector.id = "selector";
//     selector.className = "selector";
//     selector.style.top = (window.pageYOffset + (window.innerHeight * 0.05)) + "px" ;
//     document.body.appendChild(selector);
//     for (var i = 0; i < backgroundData.length; i++){
//         var img = document.createElement('div');
//         img.className = "thumbbutton";
//         img.style.backgroundImage = "url(../assets/bg/"+backgroundData[i]+")";
//         img.style.backgroundSize = "100%";
//         img.id = backgroundData[i];
//         img.addEventListener("click", function(e) {
//             document.getElementById("L2dCanvas").style.backgroundImage = "url(../assets/bg/"+this.id+")";
//             document.body.removeChild(document.getElementById("selector"));
//             document.body.removeChild(document.getElementById("darken"));
//             document.body.style.overflow = "auto";
//         }, false);
//         document.getElementById("selector").appendChild(img);
//     }
// }

